package binchunk


const (
	LUA_SIGNATURE = "\x1bLua"

	LUAC_VERSION = 0x53

	LUAC_FORMAT = 0

	LUAC_DATA = "\x19\x93\r\n\x1a\n"

	CINT_SIZE = 4

	CSIZET_SIZE = 4

	INSTRUCTION_SIZE = 4

	LUA_INTEGER_SIZE = 8

	LUA_NUMBER_SIZE = 8

	LUAC_INT = 0x5678

	LUAC_NUM = 370.5

	TAG_NIL = 0x00

	TAG_BOOLEAN = 0x01

	TAG_NUMBER = 0x03

	TAG_INTEGER = 0x13

	TAG_SHORT_STR = 0x04

	TAG_LONG_STR = 0x14
)

type header struct {

	signature	[4]byte		//签名: 0x1B4c7561-> ESC、L、u、a

	version		byte		//版本号: (大版本号、小版本号、发布号)

	format		byte		//格式号: 官方格式号为0

	luacData	[6]byte		//LUAC_DATA: 进一步校验

	// 整数和Lua虚拟机指令宽度
	cintSize	byte 		
	sizetSize	byte
	instructionSize		byte
	luaIntegerSize		byte
	luaNumberSize		byte

	luacInt		int64	// LUAC_INT: 0x000000005678, 用于检测大小端， 8个字节
	luacNum		float64 // LUAC_NUM: 浮点数370.5, 0x4077280000000000 8个字节
}

type Upvalue struct {
	Instack		byte 		// 捕获的是否是直接外围函数的局部变量，0表示否，1表示是
	Idx		byte 			// 局部变量在外围函数调用帧里的序列
}

type LocVar struct {
	VarName		string	// 局部变量名(按字符串类型存储)

	StartPC		uint32	// 开始指令索引

	EndPC		uint32	// 结束指令索引
}

type Prototype struct {

	Source		string	//源文件名 '@'开头: 从Lua源文件编译而来; '='(=stdin)开头:从标准输入编译而来; 否则, 从程序提供的字符串编译而来

	LineDefined		uint32	// 开始行号

	LastLineDefined	uint32  // 结束行号, 主函数的话，起止行号均为0, 否则均大于0

	NumParams		byte	// 固定参数个数, Lua编译器生成的主函数没有固定参数, 为0

	IsVararg		byte	// 是否是可变长参数

	MaxStackSize	byte	// 寄存器数量

	Code		[]uint32	// 指令表, 每条指令占4个字节

	Constants		[]interface{}	// 常量表

	Upvalues 		[]Upvalue 	// Upvalue表, 每个元素占两个字节

	Protos		[]*Prototype	// 子函数原型表

	LineInfo		[]uint32	// 行号表，调试信息，记录每条指令在源代码中对应的行号

	LocVars		[]LocVar 	// 局部变量表，调试信息，记录变量名

	UpvalueNames	[]string 	// Upvalue名列表，调试信息

}

type binaryChunk struct {
	header	// 头部
	sizeUpvalues byte	// 主函数upvalue的数量
	mainFunc *Prototype	// 主函数原型
}


func IsBinaryChunk(data []byte) bool {
	return len(data) > 4 &&
		string(data[:4]) == LUA_SIGNATURE
}


func Undump(data []byte) *Prototype {
	reader := &reader{data}

	reader.checkHeader()		// 校验头部

	reader.readByte()		// 跳过Upvalue数量

	return reader.readProto("")		// 读取函数原型
}